package cn.jimmie.learn.viewArt.captcha.toolbox;

import android.graphics.Path;

/**
 * function : 使用贝塞尔曲线,来构建拼图
 *
 * <p>
 * https://www.jianshu.com/p/5198d8aa80c1 (贝塞尔曲线 拟合 圆的形成)
 * http://spencermortensen.com/articles/bezier-circle/ (论文地址)
 * </p>
 * Created by jimmie.qian on 2018/12/8.
 */
class BezierUtils {
    // 画圆中 贝塞尔曲线的魔数
    private static final float MAGIC = 0.551915024494f;

    // 直线
    private static final int LINE = 4;
    // 凹半圆
    private static final int CONCAVE = 6;
    // 凸半圆
    private static final int CONVEX = 8;

    private static final int MASK = 7;

    /**
     * 获取每一边 凹凸状态
     */
    private static int getShape(int tag) {
        if (tag < LINE) return LINE;
        else if (tag < CONCAVE) return CONCAVE;
        else return CONVEX;
    }

    /**
     * 构建拼图
     *
     * @param x   起始坐标
     * @param y   .
     * @param gap 构建拼图模块的间隙
     * @param tag 随机数值, 0 - 2^12 之间, 表示随机构建一个拼图模块
     */
    static void drawPuzzle(Path path, int x, int y, float gap, int tag) {
        float radius = gap / 2;

        path.lineTo(0, 0);
        path.moveTo(x, y);

        // 绘制上边缘
        int top = getShape((tag) & MASK);
        if (top == LINE) path.lineTo(x + gap * 3, y);
        else {
            path.lineTo(x + gap, y);
            drawBezierCircleHori(path, x + gap, y, radius, top == CONVEX, true);
            path.lineTo(x + gap * 3, y);
        }

        // 绘制右边缘
        int right = getShape((tag >> 3) & MASK);
        if (right == LINE) path.lineTo(x + gap * 3, y + gap * 3);
        else {
            path.lineTo(x + gap * 3, y + gap);
            drawBezierCircleVert(path, x + gap * 3, y + gap, radius, right == CONVEX, true);
            path.lineTo(x + gap * 3, y + gap * 3);
        }

        // 绘制下边缘
        int bottom = getShape((tag >> 6) & MASK);
        if (bottom == LINE) path.lineTo(x, y + gap * 3);
        else {
            path.lineTo(x + gap * 2, y + gap * 3);
            drawBezierCircleHori(path, x + gap * 2, y + gap * 3, radius, bottom == CONVEX, false);
            path.lineTo(x, y + gap * 3);
        }

        // 绘制左边缘
        int left = getShape((tag >> 9) & MASK);
        if (left == LINE) path.lineTo(x, y);
        else {
            path.lineTo(x, y + gap * 2);
            drawBezierCircleVert(path, x, y + gap * 2, radius, left == CONVEX, false);
            path.lineTo(x, y);
        }
        // 使 区域闭合, 不然会出问题
        path.close();
    }

    /**
     * 画竖直方向的半圆
     *
     * @param path        路径
     * @param x           起始坐标 x
     * @param y           起始坐标 y
     * @param radius      圆半径
     * @param isConvex    是否 是凸的
     * @param isClockwise 笔顺 是否是顺时针
     */
    private static void drawBezierCircleVert(Path path, float x, float y, float radius, boolean isConvex, boolean isClockwise) {
        // clockwise 凸
        if (isClockwise && isConvex) {
            path.cubicTo(x + radius * (MAGIC), y, x + radius, y + radius * (MAGIC), x + radius, y + radius);
            path.cubicTo(x + radius, y + radius * (MAGIC + 1), x + radius * (MAGIC), y + radius * 2, x, y + radius * 2);
            return;
        }
        // non-clockwise 凸
        if (!isClockwise && isConvex) {
            path.cubicTo(x + radius * (MAGIC), y, x + radius, y + radius * (MAGIC - 1), x + radius, y - radius);
            path.cubicTo(x + radius, y + radius * (MAGIC - 2), x + radius * (MAGIC), y - radius * 2, x, y - radius * 2);
            return;
        }
        // clockwise 凹
        if (isClockwise) {
            path.cubicTo(x - radius * MAGIC, y, x - radius, y + radius * (1 - MAGIC), x - radius, y + radius);
            path.cubicTo(x - radius, y + radius * (1 + MAGIC), x - radius * MAGIC, y + radius * 2, x, y + radius * 2);
            return;
        }
        // non-clockwise 凹
        path.cubicTo(x - radius * MAGIC, y, x - radius, y + radius * (MAGIC - 1), x - radius, y - radius);
        path.cubicTo(x - radius, y - radius * (1 + MAGIC), x - radius * MAGIC, y - radius * 2, x, y - radius * 2);
    }

    private static void drawBezierCircleHori(Path path, float x, float y, float radius, boolean isConvex, boolean isClockwise) {

        // clockwise 凸
        if (isClockwise && isConvex) {
            path.cubicTo(x, y - radius * MAGIC, x + radius * (1 - MAGIC), y - radius, x + radius, y - radius);
            path.cubicTo(x + radius * (1 + MAGIC), y - radius, x + radius * 2, y + radius * (MAGIC - 1), x + radius * 2, y);
            return;
        }
        // non-clockwise 凸
        if (!isClockwise && isConvex) {
            path.cubicTo(x, y + radius * (MAGIC - 1), x + radius * (MAGIC - 1), y - radius, x - radius, y - radius);
            path.cubicTo(x - radius * (1 + MAGIC), y - radius, x - radius * 2, y - radius * MAGIC, x - radius * 2, y);
            return;
        }
        // clockwise 凹
        if (isClockwise) {
            path.cubicTo(x, y + radius * MAGIC, x + radius * (1 - MAGIC), y + radius, x + radius, y + radius);
            path.cubicTo(x + radius * (1 + MAGIC), y + radius, x + radius * 2, y + radius * MAGIC, x + radius * 2, y);
            return;
        }
        // non-clockwise 凹
        path.cubicTo(x, y + radius * MAGIC, x + radius * (MAGIC - 1), y + radius, x - radius, y + radius);
        path.cubicTo(x - radius * (1 + MAGIC), y + radius, x - radius * 2, y + radius * MAGIC, x - radius * 2, y);
    }
}
